package cn.cyx.demo.chat;

import cn.cyx.demo.chat.handler.HttpRequestHandler;
import cn.cyx.demo.chat.handler.WebSocketHandler;
import cn.cyx.demo.chat.service.ChatService;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Netty聊天服务器 - 聊天室服务器主类
 * 
 * 这个类是聊天室服务器的入口点，负责：
 * 1. 启动Netty服务器，监听指定端口
 * 2. 配置Netty管道，添加各种处理器
 * 3. 处理HTTP请求和WebSocket升级
 * 4. 管理服务器生命周期
 * 
 * 技术架构：
 * - 使用Netty 4.x作为网络框架
 * - 支持HTTP和WebSocket协议
 * - 采用事件驱动模型
 * - 支持优雅关闭
 * 
 * 管道配置：
 * 1. HttpServerCodec: HTTP编解码
 * 2. HttpObjectAggregator: HTTP消息聚合
 * 3. ChunkedWriteHandler: 支持大文件传输
 * 4. WebSocketServerProtocolHandler: WebSocket协议处理
 * 5. HttpRequestHandler: HTTP请求处理
 * 6. WebSocketHandler: WebSocket消息处理
 * 
 * @author cyx
 * @since 1.0.0
 */
public class ChatServer {

    /**
     * 日志记录器
     * 用于记录服务器启动、关闭和运行状态的详细日志
     */
    private static final Logger logger = LoggerFactory.getLogger(ChatServer.class);

    /**
     * 服务器监听端口
     * 默认8080，可通过命令行参数修改
     */
    private final int port;

    /**
     * 聊天服务实例
     * 负责处理所有聊天相关的业务逻辑
     */
    private final ChatService chatService;

    /**
     * 构造函数
     * 
     * 初始化聊天服务器，包括：
     * 1. 设置监听端口
     * 2. 创建聊天服务实例
     * 
     * @param port 服务器监听端口
     */
    public ChatServer(int port) {
        this.port = port;
        this.chatService = new ChatService();
    }

    /**
     * 启动聊天服务器
     * 
     * 这是服务器的核心启动方法，完成以下操作：
     * 1. 创建Netty事件循环组（boss和worker）
     * 2. 配置服务器引导程序
     * 3. 设置管道处理器链
     * 4. 绑定端口并启动服务器
     * 5. 添加关闭钩子，确保优雅关闭
     * 6. 等待服务器关闭
     * 
     * 管道处理器说明：
     * - HttpServerCodec: HTTP请求/响应编解码
     * - HttpObjectAggregator: 将HTTP消息聚合成完整对象
     * - ChunkedWriteHandler: 支持大文件传输
     * - WebSocketServerProtocolHandler: 处理WebSocket握手和协议
     * - HttpRequestHandler: 处理静态文件请求
     * - WebSocketHandler: 处理WebSocket消息
     * 
     * @throws Exception 启动异常
     */
    public void start() throws Exception {
        // 创建Netty事件循环组
        // bossGroup: 负责接受客户端连接
        // workerGroup: 负责处理客户端请求
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            // 创建服务器引导程序
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();

                            // HTTP编解码器：处理HTTP请求和响应
                            pipeline.addLast(new HttpServerCodec());

                            // HTTP消息聚合器：将多个HTTP消息片段聚合成完整消息
                            pipeline.addLast(new HttpObjectAggregator(65536));

                            // 支持大文件传输的处理器
                            pipeline.addLast(new ChunkedWriteHandler());

                            // WebSocket协议处理器（必须在HTTP处理器之前）
                            // 负责处理WebSocket握手和协议升级
                            pipeline.addLast(new WebSocketServerProtocolHandler("/ws", null, true));

                            // HTTP请求处理器（处理非WebSocket请求）
                            // 负责提供静态文件服务
                            pipeline.addLast(new HttpRequestHandler());

                            // WebSocket消息处理器
                            // 负责处理WebSocket连接后的消息
                            pipeline.addLast(new WebSocketHandler(chatService));
                        }
                    })
                    .option(ChannelOption.SO_BACKLOG, 128) // 设置连接队列大小
                    .childOption(ChannelOption.SO_KEEPALIVE, true); // 启用TCP保活

            // 绑定端口并启动服务器
            ChannelFuture future = bootstrap.bind(port).sync();
            logger.info("聊天服务器启动成功，监听端口: {}", port);
            logger.info("访问地址: http://localhost:{}", port);
            logger.info("WebSocket地址: ws://localhost:{}/ws", port);

            // 添加关闭钩子，确保程序退出时能正确关闭
            // 当JVM收到关闭信号时，会执行这个钩子
            Runtime.getRuntime().addShutdownHook(new Thread(() -> {
                logger.info("正在关闭聊天服务器...");
                try {
                    // 关闭所有用户连接
                    chatService.closeAllConnections();
                    // 关闭服务器通道
                    future.channel().close().sync();
                    logger.info("聊天服务器已关闭");
                } catch (Exception e) {
                    logger.error("关闭服务器时发生错误", e);
                }
            }));

            // 等待服务器关闭
            // 这行代码会阻塞，直到服务器被关闭
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            // 处理启动异常
            if (e.getCause() instanceof java.net.BindException) {
                logger.error("端口 {} 已被占用，请检查是否有其他服务正在运行", port);
                logger.error("可以使用以下命令查看端口占用情况：netstat -ano | findstr :{}", port);
            } else {
                logger.error("服务器启动失败", e);
            }
            throw e;
        } finally {
            // 优雅关闭EventLoopGroup
            // 设置超时时间，确保资源能够正确释放
            logger.info("正在关闭EventLoopGroup...");
            bossGroup.shutdownGracefully(0, 5, java.util.concurrent.TimeUnit.SECONDS);
            workerGroup.shutdownGracefully(0, 5, java.util.concurrent.TimeUnit.SECONDS);
            logger.info("EventLoopGroup已关闭");
        }
    }

    /**
     * 程序入口点
     * 
     * 聊天室服务器的主方法，负责：
     * 1. 解析命令行参数，获取监听端口
     * 2. 创建聊天服务器实例
     * 3. 启动服务器
     * 
     * 使用方法：
     * - 默认端口：java -jar chat-server.jar
     * - 指定端口：java -jar chat-server.jar 9090
     * 
     * @param args 命令行参数，第一个参数为端口号（可选）
     * @throws Exception 启动异常
     */
    public static void main(String[] args) throws Exception {
        // 设置默认端口
        int port = 8080;

        // 如果提供了命令行参数，使用指定的端口
        if (args.length > 0) {
            port = Integer.parseInt(args[0]);
        }

        // 创建并启动聊天服务器
        ChatServer server = new ChatServer(port);
        server.start();
    }
}